#region Copyright (c) 2006-2009 Widgetsphere LLC, All Rights Reserved
//--------------------------------------------------------------------- *
//                          Widgetsphere  LLC                           *
//             Copyright (c) 2006-2009 All Rights reserved              *
//                                                                      *
//                                                                      *
//This file and its contents are protected by United States and         *
//International copyright laws.  Unauthorized reproduction and/or       *
//distribution of all or any portion of the code contained herein       *
//is strictly prohibited and will result in severe civil and criminal   *
//penalties.  Any violations of this copyright will be prosecuted       *
//to the fullest extent possible under law.                             *
//                                                                      *
//THE SOURCE CODE CONTAINED HEREIN AND IN RELATED FILES IS PROVIDED     *
//TO THE REGISTERED DEVELOPER FOR THE PURPOSES OF EDUCATION AND         *
//TROUBLESHOOTING. UNDER NO CIRCUMSTANCES MAY ANY PORTION OF THE SOURCE *
//CODE BE DISTRIBUTED, DISCLOSED OR OTHERWISE MADE AVAILABLE TO ANY     *
//THIRD PARTY WITHOUT THE EXPRESS WRITTEN CONSENT OF WIDGETSPHERE LLC   *
//                                                                      *
//UNDER NO CIRCUMSTANCES MAY THE SOURCE CODE BE USED IN WHOLE OR IN     *
//PART, AS THE BASIS FOR CREATING A PRODUCT THAT PROVIDES THE SAME, OR  *
//SUBSTANTIALLY THE SAME, FUNCTIONALITY AS ANY WIDGETSPHERE PRODUCT.    *
//                                                                      *
//THE REGISTERED DEVELOPER ACKNOWLEDGES THAT THIS SOURCE CODE           *
//CONTAINS VALUABLE AND PROPRIETARY TRADE SECRETS OF WIDGETSPHERE,      *
//INC.  THE REGISTERED DEVELOPER AGREES TO EXPEND EVERY EFFORT TO       *
//INSURE ITS CONFIDENTIALITY.                                           *
//                                                                      *
//THE END USER LICENSE AGREEMENT (EULA) ACCOMPANYING THE PRODUCT        *
//PERMITS THE REGISTERED DEVELOPER TO REDISTRIBUTE THE PRODUCT IN       *
//EXECUTABLE FORM ONLY IN SUPPORT OF APPLICATIONS WRITTEN USING         *
//THE PRODUCT.  IT DOES NOT PROVIDE ANY RIGHTS REGARDING THE            *
//SOURCE CODE CONTAINED HEREIN.                                         *
//                                                                      *
//THIS COPYRIGHT NOTICE MAY NOT BE REMOVED FROM THIS FILE.              *
//--------------------------------------------------------------------- *
#endregion
#region Copyright (c) 2006-2009 Widgetsphere LLC, All Rights Reserved
//--------------------------------------------------------------------- *
//                          Widgetsphere  LLC                           *
//             Copyright (c) 2006-2009 All Rights reserved              *
//                                                                      *
//                                                                      *
//This file and its contents are protected by United States and         *
//International copyright laws.  Unauthorized reproduction and/or       *
//distribution of all or any portion of the code contained herein       *
//is strictly prohibited and will result in severe civil and criminal   *
//penalties.  Any violations of this copyright will be prosecuted       *
//to the fullest extent possible under law.                             *
//                                                                      *
//THE SOURCE CODE CONTAINED HEREIN AND IN RELATED FILES IS PROVIDED     *
//TO THE REGISTERED DEVELOPER FOR THE PURPOSES OF EDUCATION AND         *
//TROUBLESHOOTING. UNDER NO CIRCUMSTANCES MAY ANY PORTION OF THE SOURCE *
//CODE BE DISTRIBUTED, DISCLOSED OR OTHERWISE MADE AVAILABLE TO ANY     *
//THIRD PARTY WITHOUT THE EXPRESS WRITTEN CONSENT OF WIDGETSPHERE LLC   *
//                                                                      *
//UNDER NO CIRCUMSTANCES MAY THE SOURCE CODE BE USED IN WHOLE OR IN     *
//PART, AS THE BASIS FOR CREATING A PRODUCT THAT PROVIDES THE SAME, OR  *
//SUBSTANTIALLY THE SAME, FUNCTIONALITY AS ANY WIDGETSPHERE PRODUCT.    *
//                                                                      *
//THE REGISTERED DEVELOPER ACKNOWLEDGES THAT THIS SOURCE CODE           *
//CONTAINS VALUABLE AND PROPRIETARY TRADE SECRETS OF WIDGETSPHERE,      *
//INC.  THE REGISTERED DEVELOPER AGREES TO EXPEND EVERY EFFORT TO       *
//INSURE ITS CONFIDENTIALITY.                                           *
//                                                                      *
//THE END USER LICENSE AGREEMENT (EULA) ACCOMPANYING THE PRODUCT        *
//PERMITS THE REGISTERED DEVELOPER TO REDISTRIBUTE THE PRODUCT IN       *
//EXECUTABLE FORM ONLY IN SUPPORT OF APPLICATIONS WRITTEN USING         *
//THE PRODUCT.  IT DOES NOT PROVIDE ANY RIGHTS REGARDING THE            *
//SOURCE CODE CONTAINED HEREIN.                                         *
//                                                                      *
//THIS COPYRIGHT NOTICE MAY NOT BE REMOVED FROM THIS FILE.              *
//--------------------------------------------------------------------- *
#endregion
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using Widgetsphere.Generator.Models;
using Widgetsphere.Generator.Common.Util;
using System.Collections;
using System.Collections.ObjectModel;
using Widgetsphere.Generator.ProjectItemGenerators;
using Widgetsphere.Generator.Common.GeneratorFramework;

namespace Widgetsphere.Generator.UnitTests.UnitTestObject
{
	class UnitTestObjectGeneratedTemplate : BaseUnitTestTemplate
	{
		private StringBuilder sb = new StringBuilder();
		private Table _currentTable;

		public UnitTestObjectGeneratedTemplate(ModelRoot model, Table currentTable)
		{
			_model = model;
			_currentTable = currentTable;
		}

		#region BaseClassTemplate overrides
		public override string FileName
		{
			get
			{
				return string.Format("{0}Test.Generated.cs", _currentTable.PascalName);
			}
		}

		public string ParentItemName
		{
			get
			{
				return string.Format("{0}Test.cs", _currentTable.PascalName);
			}
		}

		public override string FileContent
		{
			get
			{
				GenerateContent();
				return sb.ToString();
			}
		}
		#endregion

		#region GenerateContent

		private void GenerateContent()
		{
			try
			{
				ValidationHelper.AppendCopyrightInCode(sb, _model);
				this.AppendUsingStatements();
				sb.AppendLine("namespace " + DefaultNamespace);
				sb.AppendLine("{");
				this.AppendClass();
				sb.AppendLine("}");
			}
			catch (Exception ex)
			{
				System.Windows.Forms.MessageBox.Show(ex.ToString());
				throw;
			}
		}

		#endregion

		#region namespace / objects

		public void AppendUsingStatements()
		{
			sb.AppendLine("using System;");
			sb.AppendLine("using System.Text;");
			sb.AppendLine("using System.Collections.Generic;");
			sb.AppendLine("using System.Linq;");			
			sb.AppendLine("using System.Configuration;");
			sb.AppendLine("using NUnit.Framework;");
			sb.AppendLine("using " + _model.CompanyName + "." + _model.ProjectName + ".Business.Objects;");
			sb.AppendLine("using " + _model.CompanyName + "." + _model.ProjectName + ".Business;");
			sb.AppendLine();
		}

		private void AppendClass()
		{
			try
			{
				sb.AppendLine("		/// <summary>");
				sb.AppendLine("		/// Unit test for the " + _currentTable.PascalName + " object.");
				sb.AppendLine("		/// </summary>");
				sb.AppendLine("	[TestFixture]");
				sb.AppendLine("	public partial class " + _currentTable.PascalName + "Test");
				sb.AppendLine("	{");
				this.AppendClassBody();
				if (_currentTable.AllowUnitTest == Table.UnitTestSettingsConstants.FullTest)
				{
					this.AppendClassBodyGuts();
				}
				sb.AppendLine("	}");
			}
			catch (Exception ex)
			{
				throw;
			}
		}

		private void AppendClassBody()
		{
			sb.AppendLine("		/// <summary>");
			sb.AppendLine("		/// Unit test for the " + _currentTable.PascalName + " object.");
			sb.AppendLine("		/// </summary>");
			sb.AppendLine("		public " + _currentTable.PascalName + "Test()");
			sb.AppendLine("		{");
			sb.AppendLine("			" + _model.CompanyName + "." + _model.ProjectName + ".Business.ConfigurationValues.GetInstance().ConnectionString = GlobalUnitTestObject.GetConnectionString();");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		partial void BeforeTests();");
			sb.AppendLine();
			sb.AppendLine("		partial void AfterTests();");
			sb.AppendLine();

			sb.AppendLine("		/// <summary>");
			sb.AppendLine("		/// Run all tests for the type " + _currentTable.PascalName + ".");
			sb.AppendLine("		/// </summary>");
			sb.AppendLine("		[Test]");
			sb.AppendLine("		public void RunTests()");
			sb.AppendLine("		{");
			sb.AppendLine("			this.BeforeTests();");
			if (_currentTable.AllowUnitTest == Table.UnitTestSettingsConstants.FullTest)
			{
				sb.AppendLine("			this.InsertItem();");
				sb.AppendLine("			this.SelectItem();");
				sb.AppendLine("			this.UpdateItem();");
				sb.AppendLine("			this.SelectItem();");
				sb.AppendLine("			this.DeleteItem();");
			}
			sb.AppendLine("			this.AfterTests();");
			sb.AppendLine("		}");
			sb.AppendLine();

		}

		private void AppendClassBodyGuts()
		{
			if ( _currentTable.Immutable || _currentTable.IsTypeTable)
				return;

			//Get all columns for full inheritence chain
			ColumnCollection columnList = _currentTable.GetColumnsFullHierarchy();

			List<Table> dependentTableList = new List<Table>();
			foreach (Table t in _currentTable.UnitTestDependencies)
			{
				if (t.AllowUnitTest == Table.UnitTestSettingsConstants.FullTest)
				{
					dependentTableList.Add(t);
				}
			}

			foreach (Column column in columnList)
			{				 
				sb.AppendLine("		private " + column.GetCodeType(true) + " _" + column.CamelName + ";");
			}

			//Create dependent unit test objects
			foreach (Table t in dependentTableList)
			{
				if (t.AllowUnitTest == Table.UnitTestSettingsConstants.FullTest)
				{
					sb.AppendLine("		private " + t.PascalName + "Test _" + t.CamelName + "Test = null;");
				}
			}

			sb.AppendLine();

			sb.AppendLine("		private void InitializeValues()");
			sb.AppendLine("		{");
			foreach (Column column in columnList)
			{				
				if (ModelHelper.IsTextType(column.DataType))
					sb.AppendLine("			_" + column.CamelName + " = DataHelper.GetRandomString(" + column.Length + ");");
				else if (ModelHelper.IsIntegerType(column.DataType))
					sb.AppendLine("			_" + column.CamelName + " = (" + column.GetCodeType(false) + ")DataHelper.GetRandomLong(255);");
				else if (ModelHelper.IsDecimalType(column.DataType))
					sb.AppendLine("			_" + column.CamelName + " = (" + column.GetCodeType(true) + ")DataHelper.GetRandomFloat();");
				else if (ModelHelper.IsDateType(column.DataType))
					sb.AppendLine("			_" + column.CamelName + " = DataHelper.GetRandomDate();");
			}
			sb.AppendLine("		}");
			sb.AppendLine();

			sb.AppendLine("		private void InitializeValuesNonRelationships()");
			sb.AppendLine("		{");

			ColumnCollection columnsNotInRelations = _currentTable.GetColumnNotInRelationships();
			foreach (Column column in columnsNotInRelations)
			{
				if (!_currentTable.PrimaryKeyColumns.Contains(column))
				{
					if (ModelHelper.IsTextType(column.DataType))
						sb.AppendLine("			_" + column.CamelName + " = DataHelper.GetRandomString(" + column.Length + ");");
					else if (ModelHelper.IsIntegerType(column.DataType))
						sb.AppendLine("			_" + column.CamelName + " = (" + column.GetCodeType(false) + ")DataHelper.GetRandomLong(255);");
					else if (ModelHelper.IsDecimalType(column.DataType))
						sb.AppendLine("			_" + column.CamelName + " = (" + column.GetCodeType(true) + ")DataHelper.GetRandomFloat();");
					else if (ModelHelper.IsDateType(column.DataType))
						sb.AppendLine("			_" + column.CamelName + " = DataHelper.GetRandomDate();");
				}
			}
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		/// <summary>");
			sb.AppendLine("		/// Create a new object of type " + _currentTable.PascalName + " in the " + _currentTable.PascalName + "Collection.");
			sb.AppendLine("		/// </summary>");
			sb.AppendLine("		/// <returns>The newly created object.</returns>");
			sb.AppendLine("		public " + _currentTable.PascalName + " InsertItem()");
			sb.AppendLine("		{");
			sb.AppendLine("			" + _currentTable.PascalName + "Collection list = new " + _currentTable.PascalName + "Collection(\"\");");
			sb.AppendLine("			Assert.IsNotNull(list, \"The initialization while inserting '" + _currentTable.PascalName + "' failed.\");");
			sb.AppendLine("			return InsertItem(list.SubDomain);");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		/// <summary>");
			sb.AppendLine("		/// Create a new object of type " + _currentTable.PascalName + " in the " + _currentTable.PascalName + "Collection.");
			sb.AppendLine("		/// </summary>");
			sb.AppendLine("		/// <returns>The newly created object.</returns>");
			sb.AppendLine("		public " + _currentTable.PascalName + " InsertItem(SubDomain subDomain)");
			sb.AppendLine("		{");
			sb.AppendLine("			this.InitializeValues();");
			sb.AppendLine("			" + _currentTable.PascalName + "Collection list = (" + _currentTable.PascalName + "Collection)subDomain[Collections." + _currentTable.PascalName + "Collection];");
			sb.AppendLine();

			#region Create dependent unit test objects
			if (dependentTableList.Count > 0)
			{
				sb.AppendLine("			//Run dependencies");
				sb.AppendLine();
				foreach (Table t in dependentTableList)
				{
					if (t.AllowUnitTest == Table.UnitTestSettingsConstants.FullTest)
					{
						sb.AppendLine("			//Dependency for " + t.PascalName);
						sb.AppendLine("			_" + t.CamelName + "Test = new " + t.PascalName + "Test();");
						sb.AppendLine("			" + t.PascalName + " " + t.CamelName + " = _" + t.CamelName + "Test.InsertItem(subDomain);");
						IEnumerable<Relation> relationList = _model.Database.Relations.FindByChildTable(_currentTable, true);
						foreach (Relation relation in relationList)
						{
							List<Table> tableList = t.GetTableHierarchy();
							if (tableList.Contains((Table)relation.ParentTableRef.Object))
							{
								foreach (ColumnRelationship columnRelationship in relation.ColumnRelationships)
								{
									Column parentColumn = (Column)columnRelationship.ParentColumnRef.Object;
									Column childColumn = (Column)columnRelationship.ChildColumnRef.Object;
									sb.AppendLine("			_" + childColumn.CamelName + " = " + t.CamelName + "." + parentColumn.PascalName + ";");
								}
							}
						}
						sb.AppendLine();
					}
				}
			}
			#endregion

			if (_currentTable.AssociativeTable)
			{
				//If this is an associative table then add the keys 
				sb.Append("			" + _currentTable.PascalName + " item = list.NewItem(");
				foreach (Column column in _currentTable.PrimaryKeyColumns)
				{
					sb.Append("_" + column.CamelName);
					if (_currentTable.PrimaryKeyColumns.IndexOf(column) < _currentTable.PrimaryKeyColumns.Count - 1)
						sb.Append(", ");
				}
				sb.AppendLine(");");
			}
			else
			{
				sb.AppendLine("			" + _currentTable.PascalName + " item = list.NewItem();");
			}

			List<TableComposite> tableCompositeList = _currentTable.GetTableCompositesFullHierarchy(true);
			List<Column> fullComponentColumnList = new  List<Column>();
			foreach (TableComposite tableComposite in tableCompositeList)
			{
				fullComponentColumnList.AddRange(_currentTable.CompositeList.GetAllColumns());
			}

			foreach (Column column in columnList)
			{
				var q = from x in fullComponentColumnList
								where x.Key == column.Key
								select x;

				//Do not set primary keys
				if (!_currentTable.PrimaryKeyColumns.Contains(column) && (q.FirstOrDefault() == null))
				{
					sb.AppendLine("			item." + column.PascalName + " = _" + column.CamelName + ";");
				}
				else if (!_currentTable.PrimaryKeyColumns.Contains(column) && (q.FirstOrDefault() != null))
				{
					var t = from w
									in tableCompositeList
									where w.Columns.Contains(column.Key)
									select w;

					TableComposite tableComposite = t.FirstOrDefault();
					if (tableComposite != null)
					{
						sb.AppendLine("			item." + tableComposite.PascalName + "Item." + column.PascalName + " = _" + column.CamelName + ";");
					}
				}
			}

			//foreach (TableComposite tableComposite in _currentTable.ComponentList)
			//{
			//  foreach (Reference reference in tableComposite.Columns)
			//  {
			//    Column column = (Column)reference.Object;
			//    sb.AppendLine("			item." + tableComposite.PascalName + "Item." + column.PascalName + " = _" + column.CamelName + ";");
			//  }
			//}

			sb.AppendLine("			list.AddItem(item);");
			sb.AppendLine("			list.Persist();");

			foreach (Column column in _currentTable.PrimaryKeyColumns)
			{
				sb.AppendLine("			_" + column.CamelName + " = item." + column.PascalName + ";");
			}

			sb.AppendLine();
			sb.AppendLine("			Assert.IsNotNull(item, \"The insert of '" + _currentTable.PascalName + "' failed.\");");
			//sb.AppendLine("			Assert.IsTrue(list.Count == 1, \"The insert of '" + _currentTable.PascalName + "' failed.\");");
			sb.AppendLine();
			sb.AppendLine("			return item;");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		/// <summary>");
			sb.AppendLine("		/// Updates an item of type " + _currentTable.PascalName + ".");
			sb.AppendLine("		/// </summary>");			
			sb.AppendLine("		public void UpdateItem()");
			sb.AppendLine("		{");
			sb.AppendLine("			//Select the item");
			sb.AppendLine("			" + _currentTable.PascalName + " item = SelectItem();");
			sb.AppendLine();
			sb.AppendLine("			//Change the field values");
			sb.AppendLine("			this.InitializeValuesNonRelationships();");
			sb.AppendLine();
			sb.AppendLine("			//Now set the new non-relationship values");
			
			foreach (Column column in columnList)
			{
				var q = from x in fullComponentColumnList 
								where x.Key == column.Key
								select x;

				//Do not set primary keys
				if (!_currentTable.PrimaryKeyColumns.Contains(column) && (q.FirstOrDefault() == null))
				{
					sb.AppendLine("			item." + column.PascalName + " = _" + column.CamelName + ";");
				}
			}

			foreach(TableComposite tableComposite in _currentTable.CompositeList)
			{
				foreach (Reference reference in tableComposite.Columns)
				{
					Column column = (Column)reference.Object;
					sb.AppendLine("			item." + tableComposite.PascalName + "Item." + column.PascalName + " = _" + column.CamelName + ";");
				}
			}

			sb.AppendLine();
			sb.AppendLine("			//Save back to database");
			sb.AppendLine("			item.Persist();");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		/// <summary>");
			sb.AppendLine("		/// Selects an item of type " + _currentTable.PascalName + ".");
			sb.AppendLine("		/// </summary>");
			sb.AppendLine("		public " + _currentTable.PascalName + " SelectItem()");
			sb.AppendLine("		{");

			sb.AppendLine("			//Select item by PK");
			sb.Append("			" + _currentTable.PascalName + " item = " + _currentTable.PascalName + ".SelectUsingPK(");
			foreach (Column column in _currentTable.PrimaryKeyColumns)
			{
				sb.Append("_" + column.CamelName);
				if (_currentTable.PrimaryKeyColumns.IndexOf(column) < _currentTable.PrimaryKeyColumns.Count - 1)
					sb.Append(", ");
			}
			sb.AppendLine(", \"\");");

			sb.AppendLine("			Assert.IsNotNull(item, \"The select of '" + _currentTable.PascalName + "' failed.\");");
			sb.AppendLine();

			sb.AppendLine("			" + _currentTable.PascalName + "Collection list = null;");
			foreach (Column column in columnList)
			{
				if (column.IsSearchable)
				{
					sb.AppendLine("			//Select items by '" + column.CamelName + "' field");
					sb.AppendLine("			list = " + _currentTable.PascalName + "Collection.SelectBy" + column.PascalName + "(_" + column.CamelName + ", \"\");");
					sb.AppendLine("			Assert.IsTrue(list.Count > 0, \"The select by " + column.PascalName + " of '" + _currentTable.PascalName + "' failed.\");");
					sb.AppendLine();
				}

				//No matter is searchable or NOT, LINQ will work
				sb.AppendLine("			//Select items by '" + column.CamelName + "' field with LINQ");
				sb.AppendLine("			list = " + _currentTable.PascalName + "Collection.RunSelect(x => x." + column.PascalName + " == _" + column.CamelName + ");");
				sb.AppendLine("			Assert.IsTrue(list.Count > 0, \"The LINQ select by " + column.PascalName + " of '" + _currentTable.PascalName + "' failed.\");");
				sb.AppendLine();
			}

			//LINQ select by ALL fields
			sb.AppendLine("			//LINQ select by all fields");
			sb.AppendLine("			list = " + _currentTable.PascalName + "Collection.RunSelect(x =>");
			int index = 0;
			foreach (Column column in columnList)
			{
				sb.Append("				x." + column.PascalName + " == _" + column.CamelName);
				if (index < columnList.Count - 1) sb.Append(" &&");
				sb.AppendLine();
				index++;
			}
			sb.AppendLine("			);");
			sb.AppendLine("			Assert.IsTrue(list.Count > 0, \"The LINQ select by all fields of '" + _currentTable.PascalName + "' failed.\");");
			sb.AppendLine();
		
			sb.AppendLine("			return item;");
			sb.AppendLine("		}");
			sb.AppendLine();
			sb.AppendLine("		/// <summary>");
			sb.AppendLine("		/// Delete an item of type " + _currentTable.PascalName + ".");
			sb.AppendLine("		/// </summary>");
			sb.AppendLine("		public void DeleteItem()");
			sb.AppendLine("		{");
			sb.AppendLine("			//Select item by PK");
			sb.AppendLine("			" + _currentTable.PascalName + " item = SelectItem();");
			sb.AppendLine();
			sb.AppendLine("			//Delete this object");
			sb.AppendLine("			" + _currentTable.PascalName + "Collection list = item.ParentCollection;");
			sb.AppendLine("			item.Delete();");
			sb.AppendLine("			Assert.IsTrue(list.Count == 0, \"Deleting '" + _currentTable.PascalName + "' failed.\");");
			sb.AppendLine();
			sb.AppendLine("			//Save back to database");
			sb.AppendLine("			list.Persist();");
			sb.AppendLine();

			if (dependentTableList.Count > 0)
			{
				sb.AppendLine("			//Run dependencies backwards");
				for (int ii = dependentTableList.Count - 1; ii >= 0; ii--)
				{
					Table t = dependentTableList[ii];
					if (t.AllowUnitTest == Table.UnitTestSettingsConstants.FullTest)
					{
						sb.AppendLine("			_" + t.CamelName + "Test.DeleteItem();");
					}
				}
			}

			sb.AppendLine("		}");
			sb.AppendLine();
		}

		#endregion

	}
}